package com.qinglei.recoup.system.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qinglei.recoup.common.domain.ActiveUser;
import com.qinglei.recoup.common.domain.CommonConstant;
import com.qinglei.recoup.common.domain.CommonRes;
import com.qinglei.recoup.common.exception.RedisConnectException;
import com.qinglei.recoup.common.service.CacheService;
import com.qinglei.recoup.common.service.RedisService;
import com.qinglei.recoup.common.tenant.TenantContextHolder;
import com.qinglei.recoup.common.utils.DateUtil;
import com.qinglei.recoup.common.utils.JsonUtils;
import com.qinglei.recoup.common.utils.RecoupUtil;
import com.qinglei.recoup.system.dao.TourLogMapper;
import com.qinglei.recoup.system.domain.Tenant;
import com.qinglei.recoup.system.domain.TourLog;
import com.qinglei.recoup.system.domain.TourStatisticsBO;
import com.qinglei.recoup.system.enums.EventEnum;
import com.qinglei.recoup.system.pojo.TourNotifyVO;
import com.qinglei.recoup.system.pojo.TourStatusVO;
import com.qinglei.recoup.system.pojo.TourTimeVO;
import com.qinglei.recoup.system.service.EventLogService;
import com.qinglei.recoup.system.service.EventWsService;
import com.qinglei.recoup.system.service.SystemConfigService;
import com.qinglei.recoup.system.service.TenantService;
import com.qinglei.recoup.system.service.TourLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author syp
 */
@Service
@Slf4j
public class TourLogServiceImpl extends ServiceImpl<TourLogMapper, TourLog> implements TourLogService {

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private RedisService redisService;

    @Resource
    private CacheService cacheService;
    @Resource
    private EventLogService eventLogService;
    @Resource
    private EventWsService eventWsService;
    @Autowired
    private TenantService tenantService;
    /**
     * 获取巡视时间表接口
     *
     * @return
     */
    @Override
    public CommonRes<List<TourStatusVO>> getListWithStatus() {
        List<TourStatusVO> tourStatusVOS = new ArrayList<>();
        Long userId = RecoupUtil.getCurrentUser().getId();
        LocalTime nowTime = LocalTime.now();
        LocalDate nowDate = LocalDate.now();
        List<TourTimeVO> tourTimeList = systemConfigService.getTourTimeList();
        if (CollectionUtils.isEmpty(tourTimeList)) {
            return CommonRes.ok(tourStatusVOS);
        }
        for (TourTimeVO tourTimeVO : tourTimeList) {
            LocalTime startTime = LocalTime.parse(tourTimeVO.getStartTime());
            LocalTime endTime = LocalTime.parse(tourTimeVO.getEndTime());
            TourStatusVO tourStatusVO = new TourStatusVO();
            tourStatusVO.setStartTime(tourTimeVO.getStartTime());
            tourStatusVO.setEndTime(tourTimeVO.getEndTime());

            if (endTime.isAfter(nowTime)) {
                tourStatusVO.setEnd(LocalDateTime.of(nowDate, endTime));
            } else {
                tourStatusVO.setEnd(LocalDateTime.of(nowDate.plusDays(1), endTime));
            }
            if (startTime.isBefore(endTime)) {
                tourStatusVO.setStart(LocalDateTime.of(tourStatusVO.getEnd().toLocalDate(), startTime));
            } else {
                tourStatusVO.setStart(LocalDateTime.of(tourStatusVO.getEnd().toLocalDate().minusDays(1), startTime));
            }

            tourStatusVOS.add(tourStatusVO);
        }

        tourStatusVOS = tourStatusVOS.stream().sorted(Comparator.comparing(TourStatusVO::getStart)).collect(Collectors.toList());

        TourStatusVO tourStatusVOFirst = tourStatusVOS.get(0);
        boolean isCurTimePeriod = DateUtil.isCurTimePeriod(tourStatusVOFirst.getStartTime(), tourStatusVOFirst.getEndTime());
        if (isCurTimePeriod) {
            tourStatusVOFirst.setIsOngoing(TourStatusVO.ONGOING_YES);
            //查询当前记录是否在进行中
            TourLog tourLog = baseMapper.selectOne(Wrappers.<TourLog>lambdaQuery()
                    .eq(TourLog::getStartTime, tourStatusVOFirst.getStart())
                    .eq(TourLog::getUserId, userId));
            if (tourLog != null && tourLog.getIsSucceed() == TourLog.SUCCEED_YES) {
                tourStatusVOFirst.setIsSucceed(TourLog.SUCCEED_YES);
            }
        } else {
            long seconds = Duration.between(LocalDateTime.now(), tourStatusVOFirst.getStart()).toMillis() / 1000;
            tourStatusVOFirst.setSeconds((int) seconds);
        }

        return CommonRes.ok(tourStatusVOS);
    }


    /**
     * 护工首页 完成巡视接口
     *
     * @param startDateTimeStr
     * @return
     */
    @Override
    public CommonRes succeedTour(String startDateTimeStr) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime startDateTime = LocalDateTime.parse(startDateTimeStr, df);
        Long userId = RecoupUtil.getCurrentUser().getId();
        Integer tenantId = TenantContextHolder.getTenantId();
        TourTimeVO curTourTime = getCurTourTime(tenantId);
        if (curTourTime == null || !startDateTime.format(DateTimeFormatter.ofPattern("HH:mm")).equals(curTourTime.getStartTime())) {
            return CommonRes.failed("已经不在巡视时间范围内");
        }

        TourLog tourLog = baseMapper.selectOne(Wrappers.<TourLog>lambdaQuery()
                .eq(TourLog::getStartTime, startDateTime)
                .eq(TourLog::getUserId, userId));
        if (tourLog == null) {
            // 没有完成记录
            LocalDateTime endDateTime;
            LocalTime endTime = LocalTime.parse(curTourTime.getEndTime());
            if (endTime.isBefore(LocalTime.parse(curTourTime.getStartTime()))) {
                endDateTime = LocalDateTime.of(startDateTime.toLocalDate().plusDays(1), endTime);
            } else {
                endDateTime = LocalDateTime.of(startDateTime.toLocalDate(), endTime);
            }
            tourLog = new TourLog();
            tourLog.setUserId(userId);
            tourLog.setStartTime(startDateTime);
            tourLog.setEndTime(endDateTime);
        }
        //当前时间
        LocalDateTime time = LocalDateTime.now();
        tourLog.setSucceedTime(time);
        long nowSecond = tourLog.getStartTime().toEpochSecond(ZoneOffset.of("+8"));
        long endSecond = time.toEpochSecond(ZoneOffset.of("+8"));
        long absSeconds = Math.abs(nowSecond - endSecond);
        tourLog.setSpendTime((int) absSeconds);
        tourLog.setIsSucceed(1);
        boolean ret = this.save(tourLog);
        if (ret) {
            // 记录事件并推送事件消息
            eventLogService.procEventLog(null, tourLog.getUserId(),
                    null, EventEnum.TOUR_END.getCode(), null, TenantContextHolder.getTenantId());
            // 推送巡查完成消息(去掉完成推送，前端主动刷新)
//            TourTimeVO tourTimeVO = new TourTimeVO();
//            tourTimeVO.setStartTime(tourLog.getStartTime().format(DateTimeFormatter.ofPattern("HH:mm")));
//            tourTimeVO.setEndTime(tourLog.getEndTime().format(DateTimeFormatter.ofPattern("HH:mm")));
//            this.pushTourEvent(tourTimeVO, TourNotifyVO.STATUS_SUCCEED);
            return CommonRes.ok();
        }
        return CommonRes.failed("操作失败");
    }


    /**
     * 载入巡视时段到redis
     *
     * @throws RedisConnectException
     */
    @Override
    public void loadTourTimeRedisCache() throws RedisConnectException {
        List<Tenant> list = tenantService.list();
        for (Tenant tenant : list) {
            TenantContextHolder.setTenantId(tenant.getId());
            List<TourTimeVO> tourTimeList = systemConfigService.getTourTimeList();
            boolean isExistCurTourTime = false;
            for (TourTimeVO tourTimeVO : tourTimeList) {
                if (this.addTourTimeCache(tourTimeVO)) {
                    isExistCurTourTime = true;
                }
            }
            // 如果不存在当前巡视时间段
            if (!isExistCurTourTime) {
                // 删除缓存中的床位当前时段
                this.delCurTourTimeCache(tenant.getId());
            }
            TenantContextHolder.clear();
        }
    }

    /**
     * 更新巡视时间段缓存
     *
     * @param oldTourTime
     * @param newTourTime
     * @throws RedisConnectException
     */
    @Override
    public void updateTourTimeCache(TourTimeVO oldTourTime, TourTimeVO newTourTime) throws RedisConnectException {
        this.delTourTimeCache(oldTourTime);
        this.addTourTimeCache(newTourTime);
    }

    /**
     * 删除巡视时间时间段缓存
     *
     * @param tourTimeVO
     * @throws RedisConnectException
     */
    @Override
    public void delTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        this.delStartTourTimeCache(tourTimeVO);
        this.delEndTourTimeCache(tourTimeVO);
        if (tourTimeVO.equals(this.getCurTourTime(tourTimeVO.getTenantId()))) {
            this.delCurTourTimeCache(tourTimeVO.getTenantId());
            // 如果删除的是当前正在进行中的巡视，推送结束通知
            this.pushTourEvent(tourTimeVO, TourNotifyVO.STATUS_END);
        }
    }

    /**
     * 添加巡视时间时间段到缓存
     *
     * @param tourTimeVO
     * @return 是否是当前巡视时间段
     * @throws RedisConnectException
     */
    @Override
    public boolean addTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        boolean ret = false;
        // 设置巡视开始时间
        this.setStartTourTimeCache(tourTimeVO);
        // 设置巡视结束时间
        this.setEndTourTimeCache(tourTimeVO);
        // 检查是否是当前巡视时间段
        if (DateUtil.isCurTimePeriod(tourTimeVO.getStartTime(), tourTimeVO.getEndTime())) {
            ret = true;
            if (!tourTimeVO.equals(this.getCurTourTime(tourTimeVO.getTenantId()))) {
                // 设置当前巡视时段
                this.setCurTourTimeCache(tourTimeVO);
            }
        }
        return ret;
    }

    /**
     * 查询当天巡查次数
     *
     * @param userId
     * @return
     */
    @Override
    public Integer getTodayTour(Long userId) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //当前时间
        LocalDateTime time = LocalDateTime.now();
        String localTime = df.format(time);
        String substring = localTime.substring(0, 11);
        //开始时间
        LocalDateTime startTime = LocalDateTime.parse(substring + "00:00:00", df);
        List<TourLog> tourLogs = baseMapper.selectList(Wrappers.<TourLog>lambdaQuery()
                .eq(TourLog::getUserId, userId)
                .eq(TourLog::getIsSucceed, 1)
                .ge(TourLog::getStartTime, startTime));
        return tourLogs.size();
    }

    /**
     * 获取指定日期的巡查数据
     *
     * @param userId
     * @param day
     * @return
     */
    @Override
    public TourStatisticsBO getTourDataByDay(Long userId, LocalDate day) {
        List<TourLog> tourLogs = this.baseMapper.selectList(Wrappers.<TourLog>lambdaQuery()
                .eq(TourLog::getUserId, userId)
                .between(TourLog::getStartTime, day, day.plusDays(1)));
        if (CollectionUtils.isEmpty(tourLogs)) {
            return null;
        }
        int tourTotalTimes = 0;
        int tourFinishTimes = 0;
        int tourUseTime = 0;
        for (TourLog tourLog : tourLogs) {
            if (tourLog.getIsSucceed() == TourLog.SUCCEED_YES) {
                tourFinishTimes++;
                tourUseTime += tourLog.getSpendTime();
            }
            tourTotalTimes++;
        }
        TourStatisticsBO tourStatisticsBO = null;
        if (tourTotalTimes > 0) {
            tourStatisticsBO = new TourStatisticsBO(tourTotalTimes, tourFinishTimes, tourUseTime);
        }
        return tourStatisticsBO;
    }

    /**
     * 设置当前巡视时间段
     *
     * @param tourTimeVO
     * @throws RedisConnectException
     */
    private void setCurTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        redisService.hset(CommonConstant.CUR_TOUR_TIME_CACHE_PREFIX, String.valueOf(tourTimeVO.getTenantId()), JsonUtils.objectToJson(tourTimeVO));
    }

    /**
     * 删除当前巡视时间段
     *
     * @throws RedisConnectException
     */
    private void delCurTourTimeCache(Integer tenantId) throws RedisConnectException {
        redisService.hdel(CommonConstant.CUR_TOUR_TIME_CACHE_PREFIX, String.valueOf(tenantId));
    }

    /**
     * 设置巡视时间开始时间段
     *
     * @param tourTimeVO
     * @throws RedisConnectException
     */
    private void setStartTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        String startTime = tourTimeVO.getStartTime();
        LocalTime localTimeStart = LocalTime.parse(startTime);
        if (localTimeStart.isAfter(LocalTime.now())) {
            LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), localTimeStart);
            long timestamp = localDateTime.toEpochSecond(ZoneOffset.of("+8"));
            redisService.zadd(CommonConstant.TOUR_TIME_START_ZSET_PREFIX,
                    (double) timestamp, JsonUtils.objectToJson(tourTimeVO));
        } else {
            LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now().plusDays(1), localTimeStart);
            long timestamp = localDateTime.toEpochSecond(ZoneOffset.of("+8"));
            redisService.zadd(CommonConstant.TOUR_TIME_START_ZSET_PREFIX,
                    (double) timestamp, JsonUtils.objectToJson(tourTimeVO));
        }
    }

    /**
     * 删除巡视时间开始时间段
     *
     * @param tourTimeVO
     * @throws RedisConnectException
     */
    private void delStartTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        redisService.zrem(CommonConstant.BED_MONITOR_TIME_START_ZSET_PREFIX, JsonUtils.objectToJson(tourTimeVO));
    }

    /**
     * 删除巡视时间结束时间段
     *
     * @param tourTimeVO
     * @throws RedisConnectException
     */
    private void delEndTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        redisService.zrem(CommonConstant.BED_MONITOR_TIME_END_ZSET_PREFIX, JsonUtils.objectToJson(tourTimeVO));
    }

    /**
     * 设置巡视时间结束时间段
     *
     * @param tourTimeVO
     * @throws RedisConnectException
     */
    private void setEndTourTimeCache(TourTimeVO tourTimeVO) throws RedisConnectException {
        String endTime = tourTimeVO.getEndTime();
        // 结束时间减1秒
        LocalTime localTimeEnd = LocalTime.parse(endTime).minusSeconds(1);
        if (localTimeEnd.isAfter(LocalTime.now())) {
            LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), localTimeEnd);
            long timestamp = localDateTime.toEpochSecond(ZoneOffset.of("+8"));
            redisService.zadd(CommonConstant.TOUR_TIME_END_ZSET_PREFIX,
                    (double) timestamp, JsonUtils.objectToJson(tourTimeVO));
        } else {
            LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now().plusDays(1), localTimeEnd);
            long timestamp = localDateTime.toEpochSecond(ZoneOffset.of("+8"));
            redisService.zadd(CommonConstant.TOUR_TIME_END_ZSET_PREFIX,
                    (double) timestamp, JsonUtils.objectToJson(tourTimeVO));
        }
    }

    /**
     * 获取当前巡视时间段
     *
     * @return
     */
    @Override
    public TourTimeVO getCurTourTime(Integer tenantId) {
        try {
            String strVO = redisService.hget(CommonConstant.CUR_TOUR_TIME_CACHE_PREFIX, String.valueOf(tenantId));
            if (strVO != null) {
                return JsonUtils.jsonToBean(strVO, TourTimeVO.class);
            }
        } catch (RedisConnectException e) {
            log.error("getCurTourTime error", e);
        }
        return null;
    }

    /**
     * 巡视时间时间段开始时的处理
     *
     * @param tourTimeVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void procTourTimeStart(TourTimeVO tourTimeVO) throws RedisConnectException {
        log.info("procTourTimeStart param:{}", tourTimeVO);
        // 设置当前巡视时间缓存
        this.setCurTourTimeCache(tourTimeVO);
        // 重置下一次触发时间
        this.setStartTourTimeCache(tourTimeVO);

        // 记录事件
        eventLogService.procEventLog(null, null, null, EventEnum.TOUR_START.getCode(), tourTimeVO.getEndTime(), tourTimeVO.getTenantId());

        // 推送巡查开始消息
        this.pushTourEvent(tourTimeVO, TourNotifyVO.STATUS_START);
    }

    /**
     * 巡视时间时间段结束时的处理
     *
     * @param tourTimeVO
     */
    @Override
    public void procTourTimeEnd(TourTimeVO tourTimeVO) throws RedisConnectException {
        // 清除当前巡视时间缓存
        this.delCurTourTimeCache(tourTimeVO.getTenantId());
        // 重置下一次触发时间
        this.setEndTourTimeCache(tourTimeVO);

        // 推送巡查结束消息
        this.pushTourEvent(tourTimeVO, TourNotifyVO.STATUS_END);

        // 查询在线护工列表
        List<ActiveUser> onlineNurse = cacheService.getOnlineNurse();
        if (CollectionUtils.isEmpty(onlineNurse)) {
            log.info("procTourTimeEnd no online nurse");
            return;
        }
        // 计算开始和结束时间
        LocalTime startTime = LocalTime.parse(tourTimeVO.getStartTime());
        LocalTime endTime = LocalTime.parse(tourTimeVO.getEndTime());

        LocalDateTime endDateTime;
        if (endTime.isAfter(LocalTime.now())) {
            endDateTime = LocalDateTime.of(LocalDate.now().minusDays(1), endTime);
        } else {
            endDateTime = LocalDateTime.of(LocalDate.now(), endTime);
        }
        LocalDateTime startDateTime;
        if (endTime.isBefore(startTime)) {
            startDateTime = LocalDateTime.of(endDateTime.toLocalDate().minusDays(1), startTime);
        } else {
            startDateTime = LocalDateTime.of(endDateTime.toLocalDate(), startTime);
        }

        // 查询已完成巡视的护工列表
        List<TourLog> tourLogs = baseMapper.selectList(Wrappers.<TourLog>lambdaQuery()
                .eq(TourLog::getStartTime, startDateTime));
        Map<Long, TourLog> tourLogMap = tourLogs.stream().collect(Collectors.toMap(TourLog::getUserId, item -> item));

        for (ActiveUser nurse : onlineNurse) {
            TourLog tourLog = tourLogMap.get(nurse.getUserId());
            if (tourLog == null) {
                tourLog = new TourLog();
                tourLog.setStartTime(startDateTime);
                tourLog.setEndTime(endDateTime);
                tourLog.setUserId(nurse.getUserId());
                tourLog.setIsSucceed(TourLog.SUCCEED_NO);
                baseMapper.insert(tourLog);
            } else if (tourLog.getIsSucceed() == TourLog.SUCCEED_YES) {
                continue;
            }
            // 记录事件并推送事件消息
            eventLogService.procEventLog(null, tourLog.getUserId(),
                    null, EventEnum.TOUR_UNFINISHED.getCode(), tourTimeVO.getEndTime(), tourTimeVO.getTenantId());
        }
    }

    /**
     * 推送巡查通知消息
     *
     * @param tourTimeVO
     * @param status
     */
    @Override
    public void pushTourEvent(TourTimeVO tourTimeVO, Integer status) {
        TourNotifyVO tourNotifyVO = new TourNotifyVO();
        tourNotifyVO.setStartTime(tourTimeVO.getStartTime());
        tourNotifyVO.setEndTime(tourTimeVO.getEndTime());
        tourNotifyVO.setStatus(status);
        tourNotifyVO.setTenantId(tourTimeVO.getTenantId());
        eventWsService.pushTourEvent(tourNotifyVO);
    }

}
